/**
 * \file    coder.cpp
 * \brief   Action (de)coder.
 * \author  Jérôme LAURENS
 * \version 0.1
 * \date    November 20, 2014
 *
 * Reads a LALR(1) grammar, generates a parser based on a template file.
 * This is based on lemon parser generator, but has strongly been
 * revisited.
 *
 */

#include "builder.h"
#include "generator.h"

LPP_NAMESPACE_BEGIN

Generator const & Coder::generator() const {
    return builder().generator();
}
GrammarRef const & Coder::grammar() const {
    return generator().grammar();
}
ActionCode Coder::code(ActionRef const & action) const {
    ActionCode C;
    if (action->is_shift()) {
        C = action->shift_state()->index();
#ifndef NDEBUG
        assert(C<generator().number_of_states());
#endif
    } else if (action->is_reduce()) {
        C = action->reduce_rule()->index() + generator().number_of_states();
#ifndef NDEBUG
        assert(action->reduce_rule()->index()<grammar()->number_of_rules());
#endif
    } else if (action->is_error()) {
        C = generator().error_action_code();
    } else {
        DEBUG_MESSAGE("Other action: %s",Description()(action).c_str());
        C = generator().other_action_code();
    }
#ifndef NDEBUG
    ActionRef const A = this->action(C,action->lookahead());
    if (action != A) {
        DEBUG_MESSAGE("EXPECTED: %s",Description()(action).c_str());
        DEBUG_MESSAGE("BUILD:    %s",Description()(A).c_str());
        throw std::logic_error("Bad code/decode pair.");
    }
#endif
    return C;
}
ActionRef const Coder::action(ActionCode const action_code, SymbolRef const & lookahead) const {
    ActionCode code = action_code;
    if (code==generator().error_action_code()) {
        return std::make_shared<Action>(Action::Type::ERROR,lookahead);
    } else if (code==generator().other_action_code()) {
        return std::make_shared<Action>(Action::Type::OTHER,lookahead);
    } else if (code<generator().number_of_states()) {
        return std::make_shared<Action>(lookahead,generator().state_at_index(code));
    } else {
        code -= generator().number_of_states();
        if (code<grammar()->number_of_rules()) {
             return std::make_shared<Action>(lookahead,grammar()->rule_with_index(code));
        }
    }
    return std::make_shared<Action>(Action::Type::ERROR,lookahead);
}

/* Used for testing purposes only.
*/
ActionRef const Coder::action(StateRef const & state, SymbolRef const & lookahead) const {
    Transaction const T = transaction(state,lookahead);
    return action(T.action(),lookahead);// this may be inaccurate when used too early
}
Transaction const Coder::transaction(StateRef const & state, SymbolRef const & lookahead) const {
    ActionOffset offset0 = lookahead->is_terminal()?
                state->terminal_offset():
                state->non_terminal_offset();
    Index bound = std::numeric_limits<ActionOffset>().max()-offset0;
    Index lookahead_index = lookahead->index();
    if (lookahead_index<=bound) {
        ActionOffset offset1 = offset0+lookahead_index;
        if (offset1>=0) {
            Index offset = offset1;
            if (builder().action_table().count(offset)) {
                Transaction T = builder().action_table().at(offset);
                if (T.lookahead()==lookahead_index) {
                    return T;
                }
            }
        }
    }
    Transaction T(lookahead_index,builder().generator().error_action_code());
    return T;
}
#ifndef NDEBUG
ActionRef const Coder::simple_action(StateRef const & state, SymbolRef const & lookahead) const {
    Index k = state->index()*generator().grammar()->number_of_lookahead_symbols()+lookahead->index();
    if (simple_action_table().count(k)) {
        Transaction T = simple_action_table().at(k);
        ActionIndex code = T.action();
        ActionRef const action = this->action(code,lookahead);
//        DEBUG_MESSAGE("%s / %s",(Format("DECODE: %1%=>(%2%,%3%)")%k%T.lookahead()%T.action()).str().c_str(),Description()(action).c_str());
        return action;
    }
    return std::make_shared<Action>(Action::Type::ERROR,lookahead);
}
#endif

void Coder::simple_encode(StateRef const & state, ActionRef const & action) {
    SymbolIndex lookahead_index = action->lookahead()->index();
    if (lookahead_index>=grammar()->number_of_lookahead_symbols()) {
        DEBUG_MESSAGE("%s",Description()(action->lookahead(),3).c_str());
        throw std::logic_error("Inconsistent lookahead index.");
    }
    Transactions::key_type k = state->index()*grammar()->number_of_lookahead_symbols()+lookahead_index;
    ActionCode code = this->code(action);
    Transaction T(lookahead_index,code);
    DEBUG_MESSAGE("%s / %s",(Format("SIMPLE ENCODE: %1%=>(%2%,%3%)")%k%T.lookahead()%T.action()).str().c_str()
                  ,Description()(action).c_str());
    if (simple_action_table_m.count(k)) {
        DEBUG_MESSAGE("k=%lu",k);
        throw std::logic_error("Already encoded transaction (simple case).");
    }
    simple_action_table_m.emplace(k,T);
    if (!simple_action_table_m.count(k)) {
        throw std::logic_error("Missing encoded transaction (simple case).");
    } else if (T!=simple_action_table_m.at(k)) {
        Transaction const & TT = simple_action_table_m.at(k);
        Format F("%1%->(%2%,%3%)!=%1%->(%4%,%5%)");
        F%k;
        F%T.lookahead()%T.action();
        F%TT.lookahead()%TT.action();
        DEBUG_MESSAGE("%s",F.str().c_str());
        throw std::logic_error("Badly encoded transaction (simple case).");
    } else {
        ActionRef const & built = simple_action(state,action->lookahead());
        if (action!=built) {
            DEBUG_MESSAGE("EXPECTED: %s",Description()(action).c_str());
            DEBUG_MESSAGE("BUILT:    %s",Description()(built).c_str());
            throw std::logic_error("Badly encoded action (simple case - 2).");
        }
    }
}

LPP_NAMESPACE_END

